﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LaughingDog.GameObjects;
using System.IO;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
using LaughingDog.Managers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using LaughingDog.Input;

namespace LaughingDog.Components
{        
    /// <summary>
    /// Base component for all Scripts.
    /// </summary>
    public class Script : Behaviour
    {
        //private String mSourceFile = "";
        //private String mSourceDLL = "";
        //private String mScriptTypeName = "";        

        /// <summary>
        /// Gets or sets the path to the source file this Script was compiled from. Should not be set manually.
        /// </summary>
        //public String SourceFile { get { return mSourceFile; } set { mSourceFile = value; } }

        /// <summary>
        /// Gets or sets the path to the source DLL this Script was compiled from. Should not be set manually.
        /// </summary>
        //public String SourceDLL { get { return mSourceDLL; } set { mSourceDLL = value; } }

        /// <summary>
        /// Gets or sets the name of the Script class that this Script was compiled from. Should not be set manually.
        /// </summary>
        //public String ScriptTypeName { get { return mScriptTypeName; } set { mScriptTypeName = value; } }        

        /// <summary>
        /// Creates a new instance of a Script component.
        /// </summary>
        /// <param name="gameObject">The GameObject this Component should be attached to.</param>        
        public Script(GameObject gameObject)
            : base(gameObject)
        {

        }

        public virtual void Awake()
        {
        }

        /// <summary>
        /// Updates this Script component.
        /// </summary>
        public override void Update()
        {                
            base.Update();
        }

        ///// <summary>
        ///// Called automatically when the mouse is hovering over the Collider (if any) attached to the GameObject that this Script is attached to.
        ///// </summary>
        //public virtual void OnMouseOver()
        //{

        //}

        ///// <summary>
        ///// Called automatically when the mouse pressed down over the Collider (if any) attached to the GameObject that this Script is attached to.
        ///// </summary>        
        //public virtual void OnMouseJustPressed()
        //{

        //}

        ///// <summary>
        ///// Called automatically when the mouse pressed down over the Collider (if any) attached to the GameObject that this Script is attached to.
        ///// </summary>
        ///// <param name="button">The mouse button that was pressed.</param>
        //public virtual void OnMouseJustPressed(MouseButtons button)
        //{

        //}


        ///// <summary>
        ///// Called automatically while the mouse is held down on the Collider (if any) attached to the GameObject that this Script is attached to.
        ///// </summary>        
        //public virtual void OnMousePressed()
        //{

        //}

        ///// <summary>
        ///// Called automatically while the mouse is held down on the Collider (if any) attached to the GameObject that this Script is attached to.
        ///// </summary>
        ///// <param name="button">The mouse button that was pressed.</param>
        //public virtual void OnMousePressed(MouseButtons button)
        //{

        //}

        ///// <summary>
        ///// Called automatically when the mouse is first released when hovering over the Collider (if any) attached to the GameObject that this Script is attached to.
        ///// </summary>
        //public virtual void OnMouseJustReleased()
        //{

        //}

        ///// <summary>
        ///// Called automatically when the mouse is first released when hovering over the Collider (if any) attached to the GameObject that this Script is attached to.
        ///// </summary>
        ///// <param name="button">The mouse button that was released.</param>
        //public virtual void OnMouseJustReleased(MouseButtons button)
        //{

        //}

        ///// <summary>
        ///// Recompiles this Script component.
        ///// </summary>
        ///// <returns>The recompiled Script.</returns>
        //public Script Recompile()
        //{
        //    //Dictionary<String, object> storedPrivateFields = new Dictionary<String, object>();
        //    //Dictionary<String, object> storedPublicFields = new Dictionary<String, object>();

        //    //foreach (FieldInfo info in getPrivateFields(this))
        //    //    storedPrivateFields.Add(info.Name, info.GetValue(this));

        //    //foreach (FieldInfo info in getPublicFields(this))
        //    //    storedPublicFields.Add(info.Name, info.GetValue(this));

        //    Script recompiledScript = Script.Compile(mSourceFile, mScriptTypeName, GameObject);

        //    //Dictionary<FieldInfo, object> compiledPrivateFields = new Dictionary<FieldInfo, object>();
        //    //Dictionary<FieldInfo, object> compiledPublicFields = new Dictionary<FieldInfo, object>();

        //    //foreach (FieldInfo info in getPrivateFields(this))
        //    //    compiledPrivateFields.Add(info, info.GetValue(this));

        //    //foreach (FieldInfo info in getPublicFields(this))
        //    //    compiledPrivateFields.Add(info, info.GetValue(this));

        //    //FieldInfo[] currentPrivateFields = getPrivateFields();
        //    //FieldInfo[] currentPublicFields = getPublicFields();



        //    //setPrivateFields(recompiledScript, storedPrivateFields);
        //   // setPublicFields(recompiledScript, storedPublicFields);
         
        //    return recompiledScript;
        //}

        /// <summary>
        /// Compiles a Script component from a source file.
        /// </summary>
        /// <param name="scriptPath">The path to the script source file.</param>
        /// <param name="scriptTypeName">The Class type to use.</param>
        /// <param name="gameObject">The GameObject the component should be attached to.</param>
        /// <returns>The compiled Script component.</returns>
        public static Script Compile(String scriptPath, GameObject gameObject)
        {
            Script compiledScript = null;
            String scriptTypeName = Path.GetFileNameWithoutExtension(scriptPath);

            try
            {
                if (File.Exists(scriptPath))
                {                    
                    // Create the actual type in memory so it can be used.
                    CodeDomProvider provider = new CSharpCodeProvider();
                    CompilerParameters compilerParams = new CompilerParameters();
                    compilerParams.CompilerOptions = "/target:library";
                    compilerParams.GenerateExecutable = false;
                    compilerParams.GenerateInMemory = true;
                    compilerParams.IncludeDebugInformation = false;

                    compilerParams.ReferencedAssemblies.Add("mscorlib.dll");
                    compilerParams.ReferencedAssemblies.Add("LaughingDog.dll");
                    compilerParams.ReferencedAssemblies.Add("Microsoft.Xna.Framework.dll");
                    compilerParams.ReferencedAssemblies.Add("Microsoft.Xna.Framework.Game.dll");

                    CompilerResults result = provider.CompileAssemblyFromFile(compilerParams, scriptPath);

                    if (!result.Errors.HasErrors)
                    {
                        Type type = result.CompiledAssembly.GetType(scriptTypeName);
                        compiledScript = Activator.CreateInstance(type, gameObject) as Script;

                        if (compiledScript != null)
                        {
                            //compiledScript.SourceFile = scriptPath;
                            //compiledScript.ScriptTypeName = scriptTypeName;                            
                        }
                    }
                    else
                    {
                        foreach (CompilerError error in result.Errors)
                        {
                            System.Console.WriteLine(error.ErrorText);
                        }
                    }
                }
            }
            catch (IOException)
            {
                return Script.Compile(scriptPath, gameObject);
            }

            return compiledScript;
        }

        //public static FieldInfo[] getPrivateFields(object instance)
        //{
        //    return getFields(instance, BindingFlags.Instance | BindingFlags.NonPublic);
        //}

        //public static void setPrivateFields(object instance, Dictionary<String, object> values)
        //{            
        //    Type type = instance.GetType();

        //    foreach (FieldInfo info in getPrivateFields(instance))
        //    {
        //        try
        //        {
                    
        //            info.SetValue(instance, values[info.Name]);
        //        }
        //        catch (NullReferenceException)
        //        {
        //        }
        //    }
        //}

        //public static FieldInfo[] getPublicFields(object instance)
        //{
        //    return getFields(instance, BindingFlags.Instance | BindingFlags.Public);
        //}

        //public static void setPublicFields(object instance, Dictionary<String, object> values)
        //{
        //    Type type = instance.GetType();

        //    foreach (FieldInfo info in getPublicFields(instance))
        //    {
        //        try
        //        {
        //            info.SetValue(instance, values[info.Name]);
        //        }
        //        catch (NullReferenceException)
        //        {
        //        }            
        //    }
        //}

        //public static FieldInfo[] getFields(object instance, BindingFlags flags)
        //{
        //    Type type = instance.GetType();

        //    return type.GetFields(flags);            
        //} 
    }
}
